#include "SA.h"
#include <cmath>
#include <random>
#include <utility>

SA::SA(double startT, double endT, double alpha, int iter) {
    this->currentT = startT;
    this->endT = endT;
    this->alpha = alpha;
    this->iter = iter;
    this->bestValue = INT32_MAX;
}

void SA::run(Job& job) {
    // 外循环迭代，当前温度小于终止温度的阈值
    while (currentT > endT) {
        // 内循环迭代
        for (int i = 0; i < iter; ++i) {
            int oldTime = job.makespan(job.getRank());   // 获取当前解的加工总时间
            // 记录遇见过的所有解的最优解
            if (oldTime < bestValue) {
                bestValue = oldTime;
                bestRank = job.getRank();
            }
            auto newRank = generateNew(job.getRank());   // 获取邻域解
            // 获取邻域解的加工总时间
            int newTime = job.makespan(newRank);
            // 是否接受新值
            if (accept(oldTime, newTime)) {
                job.setRank(newRank);
            }
        }
        currentT *= alpha;  // 温度按照一定比例下降
    }
    job.setRank(bestRank);
}

bool SA::accept(int oldTime, int newTime) const {
    // 如果新值能量小于旧值则直接接受
    if (oldTime > newTime) {
        return true;
    }
    else {
        // 否则概率接受
        double p = std::exp((oldTime - newTime) / currentT);
        std::random_device rd;  // 生成随机种子
        std::mt19937 gen(rd());
        std::uniform_real_distribution<double> dis(0.0, 1.0); // 生成一个0到1的随机数
        if (dis(gen) <= p) {
            return true;
        }
        else {
            return false;
        }
    }
}

std::vector<int> SA::generateNew(std::vector<int> oldRank) {
    int i = 0, j = 0;
    // 如果i和j重复就重新随机生成
    while (i == j) {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<int> dis(0, int(oldRank.size()) - 1); // 生成一个0到n的随机数（不包含n）
        i = dis(gen), j = dis(gen); // 随机生成两个整数作为被交换的位置
    }
    std::swap(oldRank[i], oldRank[j]);
    return oldRank;
}